#ifndef __PLUGINS_MANAGER_H__
#define __PLUGINS_MANAGER_H__

#include <cstdint>
#include <string>
#include <map>
#include <typeinfo>
#include <stdexcept>
#include <memory>

#include "Log.h"
#include "Utils/Hash.hpp"

#include "GameSingleton.h"

namespace PocketSurvival
{

    enum class CmdRunType
    {
        Unknow,         // 未知
        NotFound,       // 没有找到该命令
        TooLong,        // 命令拆分过长
        NoAuth,         // 没有权限
        Success         // 成功
    };

    class IPlugin;
    class Client;
    typedef std::shared_ptr<Client> ClientPtr;

    typedef void(*CFunction)();

    struct CorePluginData
    {
        void* handler;
        CFunction initializeFunc;
        CFunction loadFunc;
        CFunction saveFunc;
    };

    class PluginsManager : public SimpleSingletonItem
    {
        friend class SimpleSingleton;
    private:
        std::map<size_t, IPlugin*> m_pluginMap;
        std::map<std::string, IPlugin*> m_cmdMap;
        std::map<std::string, CorePluginData> m_libraryPtrMap;
        
    private:
        PluginsManager();
        ~PluginsManager();
        PluginsManager(const PluginsManager &) = delete;
        PluginsManager(PluginsManager &&) = delete;
        PluginsManager &operator=(const PluginsManager &) = delete;
        PluginsManager &operator=(PluginsManager &&) = delete;

        void loadLibrary(const std::string &filePath);

    public:

        // 加载 lua 脚本插件
        // void loadLuaPluginsFiles();

        // 加载 js 脚本插件
        // void loadJsPluginsFiles();

        // 加载 sdk 编译出来得源代码插件
        void loadCorePluginsFiles();

        void load();
        void save();

        CmdRunType processCmdMessage(ClientPtr client, const std::string &message, bool isTermainl = false);

        /**
         * @brief 注册插件
         * @note 同个插件只能注册一次，二次注册会报错
        */
        template<class PluginT>
        inline void registerPlugin()
        {
            size_t hashCode = Utils::Hash::GetTypeHash<PluginT>();
            if(m_pluginMap.find(hashCode) == m_pluginMap.end())
            {
                m_pluginMap[hashCode] = new PluginT;
                return;
            }
            throw std::logic_error(fmt::format("不能重复注册插件对象 {}", Utils::Hash::ToTypeName<PluginT>()));
        }

        /**
         * @brief 查找插件
         * @return nullptr 为查找不到该插件
        */
        template<typename PluginT>
        inline PluginT *findPlugin()
        {
            size_t hashCode = Utils::Hash::GetTypeHash<PluginT>();
            auto it = m_pluginMap.find(hashCode);
            if(it != m_pluginMap.end())
            {
                return static_cast<PluginT*>(it->second);
            }
            return nullptr;
        }
    };

} // namespace PocketSurvival


#endif
